#!/usr/bin/env python
"""
@Version: python3.8
@Project: DataCrawler
@File:    asyncio_example.py
@Date:    2022/11/30 22:35
@Author:  jia
@Desc:    介绍协程异步用法

await后加三种对象：1，future(异步通常在未来得到结果) 2,task对象 3,协程(coroutine)对象
asyncio.gather()和asyncio.wait()区别：
共同点：都需要传入多个task对象，都有返回值
不同点：gather在所有任务执行完才返回结果列表， wait可以通过return_when来控制什么时候返回，且返回值有两个

asyncio.create_task()和asyncio.ensure_future()区别：
共同点：创建任务，都可将协程转为task对象,添加到事件循环中等待执行
不同点：create_task接受协程对象，ensure_future可接受协程、future对象，可以调用cancel(),add_done_callback()
注意：协程并发是通过事件循环，要创建多个task才能实现
"""
import asyncio
import time


async def f1():
    print(1)
    await asyncio.sleep(2)
    print(3)
    return '执行完f1'


async def f2():
    print(2)
    await asyncio.sleep(2)
    print(4)
    return "执行完f2"


async def f3():
    await asyncio.sleep(2)
    print('执行了f3')
    return 'f3'


async def f4(n):
    print(f'f4的参数来源于{n}')


async def main():
    """
    create_task:给协程函数包装成task对象,可设定名字，给事件设定timeout时间，设定return_when
    asyncio.gather：接收多个协程函数或task对象，并在它们都执行完成后将它们的结果封装成一个列表并返回
    """
    tasks = [
        asyncio.create_task(f1(), name="f1"),
        asyncio.create_task(f2(), name="f2")
    ]

    res = await asyncio.gather(*tasks)
    print(res)


async def main2():
    """
    asyncio.wait:传入可迭代的task对象
    返回两个参数done:执行完返回的结果 pending:未执行完的结果
    asyncio.wait(coros, *, loop=None, timeout=None, return_when=ALL_COMPLETED)
    注意：asyncio.wait(f1(), f2())  python3.8以后不接受传入协程对象，需要传入task对象
    """
    start = time.time()
    print("start")

    # 将协程转为task对象
    tasks = [asyncio.create_task(f1()), asyncio.create_task(f2())]
    done, pending = await asyncio.wait(tasks)
    for d in done:
        print("d result is :", d.result())
    for p in pending:
        print(p)

    print("end")
    end = time.time()
    print(str(end - start))


async def main3():
    """
    asyncio.gather:分组用法 支持任务分组、组任务取消
    """
    group1 = asyncio.gather(f1(), f2())
    group2 = asyncio.gather(f2(), f3())
    group1.cancel()
    # 为true时group1不执行不会报错
    res = await asyncio.gather(group1, group2, return_exceptions=True)
    print(res)


async def main4():
    """
    ensure_future：将一个 coroutine(协程)转换成一个 Task 对象，然后加入事件循环中等待执行
    """
    # 协程 转 task对象
    task1 = asyncio.ensure_future(f3())
    task2 = asyncio.ensure_future(f4(await task1))
    print(type(task1))
    # 等待执行完成,结果返回一个列表
    res = await asyncio.gather(task1, task2)
    print(res)

    # 单个输出
    result1, result2 = task1.result(), task2.result()
    print(f'result:{result1}, result2:{result2}')


if __name__ == '__main__':
    asyncio.run(main4())
